#include "DEHT.h"
#include "misc.h"
#include "generator.h"
#include <stdlib.h>
#include <time.h>

#define BUCKETS_IN_HASH_TABLE 65536
#define PAIRS_PER_BUCKET 7
#define BYTES_PER_VALIDATION_KEY 8

/*prototypes*/
/*input parse & checks*/
int checkFlag(char *Flag);

int parseFlag(char *Flag);

/*these help the prog.'s flow - helps go around an ugly 'if' in the
 * main loop (which consumes time on an already busy job)*/
typedef LONG_INDEX_PROJ (*nextFunc)(LONG_INDEX_PROJ k);

LONG_INDEX_PROJ nextIter(LONG_INDEX_PROJ k);

LONG_INDEX_PROJ nextRand(LONG_INDEX_PROJ k);

LONG_INDEX_PROJ nextK(nextFunc next, LONG_INDEX_PROJ k);

int main(int argc, char **argv){
	DEHT *ht = NULL;
	int hOutLen = 0;
	BasicHashFunctionPtr h = NULL;
	char pswdBuf[80] = {0};
	unsigned char hashBuf[80] = {0};
	int res = 0;
	LONG_INDEX_PROJ k = 0, nIterations = 0,i = 0;
	nextFunc next = NULL;
	Rule *rule = NULL;

	if (argc != 6){
		fprintf(stderr, "Error: Usage exhaustive_table_generator <rule> <dictionary> <hash> <filenames prefix> <\"all\" or number of random password>\n");
		return 0;
	}

	/*parse rule and dictionary*/
	if (checkRule(argv[1]) == 0){
		fprintf(stderr, "Error: rule %s does not fit syntax.\n", argv[1]);
		return 0;
	}
	rule = PPRule(argv[2], argv[1]);
	if (rule == NULL){
		fprintf(stderr, "mem. allocation failure\n");
		return 0;
	}
	/*parse the hash function*/
	parseHashFunction(argv[3], &h, &hOutLen);
	if (h == NULL){
		fprintf(stderr, "%s is not a known hash function\n", argv[3]);
		free_PPRule(rule);
		return 0;
	}
	/*parse Flag*/
	res = checkFlag(argv[5]);
	if (res == -1){
		nIterations = rule->blockRange[rule->numBlocks-1];
		next = nextIter;
	}
	else if (res > 0){
		nIterations = res;
		next = nextRand;
		srand(time(0));
		k = next(k);
	}
	else{
		fprintf(stderr, "Flag argument is illegal.\nuse:\"all\" or a positive integer.\n");
		free_PPRule(rule);
		return 0;
	}
	/*create a new DEHT, and work in cache mode (use read and calc)*/
	ht = create_empty_DEHT(argv[4], hash1,  hash2, argv[3], BUCKETS_IN_HASH_TABLE, PAIRS_PER_BUCKET, BYTES_PER_VALIDATION_KEY, 0);
	if (ht == NULL) {
		free_PPRule(rule);	
		return 0;
	}

	if (read_DEHT_pointers_table(ht) == DEHT_STATUS_FAIL){
		lock_DEHT_files(ht);
		free_PPRule(rule);
		return 0;
	}

	if (calc_DEHT_last_block_per_bucket(ht) == DEHT_STATUS_FAIL){
		lock_DEHT_files(ht);
		free_PPRule(rule);
		return 0;
	}

	/*Main Loop - inserting data*/
	for (k = 0, i = 0; i < nIterations; i++, k = next(k)){
		generator(k, rule, pswdBuf);
		while (strcmp(pswdBuf,"") == 0){
			k++;
			generator(k, rule, pswdBuf);		
		}
		cryptHash(h, pswdBuf, hashBuf);
		if (add_DEHT (ht, hashBuf, hOutLen, (unsigned char *)pswdBuf, strlen(pswdBuf)) == DEHT_STATUS_FAIL){
			free_PPRule(rule);
			lock_DEHT_files(ht);
			return 0;
		}
	}

	free_PPRule(rule);
	lock_DEHT_files(ht);
	return 1;
}

/*checks if the Flag arg. is a valid input ("all" string or a positive integer)
 * if not a legal input return 0 (or if Flag is 0 <=> don't iterate)
 * if the string is all return -1
 * else return the integer
 */
int checkFlag(char *Flag){
	char *p = NULL;

	if (strcmp("all", Flag) == 0){
		return -1;
	}

	/*check if the string is a legal input and convert it to int*/
	p = Flag;
	while (*p != '\0'){
		if ((*p < '0') || (*p > '9'))
			return 0;
		p++;
	}
	return atoi(Flag);
}

LONG_INDEX_PROJ nextIter(LONG_INDEX_PROJ k){
	return k+1;
}

LONG_INDEX_PROJ nextRand(LONG_INDEX_PROJ k){
	char saltBuffer[6] = {0};
	
	createSalt(saltBuffer);
	return pseudo_random_function((unsigned char *)saltBuffer ,6, pseudo_random_generator_proj(rand()) );
}

LONG_INDEX_PROJ nextK(nextFunc next, LONG_INDEX_PROJ k){
	return next(k);
}
